package com.lumberer.pay2021.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Semaphore;

import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lumberer.pay2021.wx.init.config.MerchantConfig;
import com.lumberer.pay2021.wx.jsapi.payment.entity.PayData;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
/**
 * 微信请求
 * 
 * @author lumberer
 * @date 2021年9月26日
 */
public class WxRequest {
	public static void main(String[] args) {
		 LocalDateTime currentTime =LocalDateTime.now();
	}
	//线程max
	private static Semaphore semaphore = new Semaphore(100);

	/**
	 * 下账账单请求
	 * 
	 * @param uri
	 * @param res
	 * @return
	 */
	public static String wxHttpDownload(String uri, HttpServletResponse res) {
		if (!semaphore.tryAcquire()) {
			throw new RuntimeException("并发量超过限制");
		}
		try {
			uri = uri.split("&")[0];
			// 私有信息
			PrivateKey privateKey = Pem.loadPrivateKey(MerchantConfig.keyPath);
			//
			File file = new File(MerchantConfig.wechatpayPath);
			if (!file.exists()) {
				throw new RuntimeException("证书路径错误，请确保证书路径");
			}
			InputStream in = new FileInputStream(file);
			X509Certificate certificate = PemUtil.loadCertificate(in);
			URIBuilder uriBuilder = new URIBuilder(uri);
			HttpGet httpGet = new HttpGet(uriBuilder.build());
			httpGet.addHeader("Accept", "application/json; charset=utf-8");
			httpGet.addHeader("Connection", "Keep-Alive");// 维持长连接
			List<X509Certificate> certificates = new ArrayList<>();
			certificates.add(certificate);
			WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
					.withMerchant(MerchantConfig.mchid, MerchantConfig.serialNumber, privateKey)
					.withWechatpay(certificates);
			// 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
			CloseableHttpClient httpClient = builder.withValidator(response -> true).build();
			CloseableHttpResponse responseHttp = httpClient.execute(httpGet);
			HttpEntity https = responseHttp.getEntity();
			if (uri.contains("gzip") || uri.contains("GZIP")) {
				// return bodyAsString;
				InputStream fis = null;
				try {
					fis = https.getContent();
					// byte[] buffer = new byte[fis.available()];
					// fis.read(buffer);z
					// 文件名
					String fileName = "账单.gzip";
					// 解决文件乱码
					fileName = URLEncoder.encode(fileName, "UTF8");
					// 下载文件
					res.setContentType("application/octet-stream");
					res.setHeader("Content-disposition", "attachment;filename=" + fileName);
					byte[] bufferData = new byte[1024];
					while (fis.read(bufferData) > 0) {
						res.getOutputStream().write(bufferData);
					}

					res.flushBuffer();

					return null;
				} catch (Exception e) {
					// TODO: handle exception
					throw new RuntimeException("异常");
				} finally {
					if (fis != null) {
						fis.close();
					}
				}
			} else {
				String bodyAsString = EntityUtils.toString(https);
				// return bodyAsString;
				return bodyAsString;
			}

		} catch (IOException io) {
			// TODO: handle exception
			io.printStackTrace();
			throw new RuntimeException("文件证书异常", io);
		} catch (URISyntaxException uriException) {
			throw new RuntimeException("请求路径异常", uriException);
		}finally{
			semaphore.release();
		}

	}
	/**
	 * 发送微信的HTTP请求，并自动进行验签
	 * 
	 * @param uri
	 *            请求地址
	 * @param data
	 *            集合参数
	 * @param keyPath
	 *            API安全密钥证书路径
	 * @param wechatpayPath
	 *            微信平台证书
	 * @param serialNumber
	 *            API安全证书序号
	 * @return
	 */
	public static JSONObject wxHttpPost(String uri, Object data) {
		if (!semaphore.tryAcquire()) {
			throw new RuntimeException("并发量超过限制");
		}
		try {
			// 私有信息
			PrivateKey privateKey = Pem.loadPrivateKey(MerchantConfig.keyPath);
			//
			File file = new File(MerchantConfig.wechatpayPath);
			if (!file.exists()) {
				throw new RuntimeException("证书路径错误，请确保证书路径");
			}
			InputStream in = new FileInputStream(file);
			X509Certificate certificate = PemUtil.loadCertificate(in);

			HttpPost httpPost = new HttpPost(uri);
			httpPost.addHeader("Accept", "application/json");

			httpPost.addHeader("Content-type", "application/json; charset=utf-8");

			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.writeValue(bos, data);
			httpPost.setEntity(new StringEntity(bos.toString("UTF-8"),"UTF-8"));
			
			List<X509Certificate> certificates = new ArrayList<>();
			certificates.add(certificate);
			WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
					.withMerchant(MerchantConfig.mchid, MerchantConfig.serialNumber, privateKey)
					.withWechatpay(certificates);
			// 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
			CloseableHttpClient httpClient = builder.build();

			CloseableHttpResponse response = httpClient.execute(httpPost);

			String bodyAsString = EntityUtils.toString(response.getEntity());
			JSONObject resultJson = JSONObject.parseObject(bodyAsString);
			return resultJson;
		} catch (IOException io) {
			// TODO: handle exception
			throw new RuntimeException("文件证书异常", io);
		}finally {
			semaphore.release();
		}

	}

	/**
	 * 微信GET请求
	 * 
	 * @param uri
	 * @param data
	 * @param keyPath
	 * @param wechatpayPath
	 * @param serialNumber
	 * @return
	 */
	public static String wxHttpGet(String uri) {
		if (!semaphore.tryAcquire()) {
			throw new RuntimeException("并发量超过限制");
		}
		try {
			// 私有信息
			PrivateKey privateKey = Pem.loadPrivateKey(MerchantConfig.keyPath);
			//
			File file = new File(MerchantConfig.wechatpayPath);
			if (!file.exists()) {
				throw new RuntimeException("证书路径错误，请确保证书路径");
			}
			InputStream in = new FileInputStream(file);
			X509Certificate certificate = PemUtil.loadCertificate(in);
			URIBuilder uriBuilder = new URIBuilder(uri);
			HttpGet httpGet = new HttpGet(uriBuilder.build());
			httpGet.addHeader("Accept", "application/json");
			
			List<X509Certificate> certificates = new ArrayList<>();
			certificates.add(certificate);
			WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
					.withMerchant(MerchantConfig.mchid, MerchantConfig.serialNumber, privateKey)
					.withWechatpay(certificates);
			// 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
			CloseableHttpClient httpClient = builder.build();

			CloseableHttpResponse response = httpClient.execute(httpGet);
			String bodyAsString = EntityUtils.toString(response.getEntity());
			return bodyAsString;
		} catch (IOException io) {
			// TODO: handle exception
			throw new RuntimeException("文件证书异常", io);
		} catch (URISyntaxException uriException) {
			throw new RuntimeException("请求路径异常", uriException);
		}finally {
			semaphore.release();
		}

	}

	/**
	 * 加密返回paySign
	 * 
	 * @param appid
	 *            平台号
	 * @param prepay_id
	 *            预订单号
	 * @param keyPath
	 *            商户密钥路径
	 * @return
	 */
	public static PayData paySign(String appid, String prepay_id, String keyPath) {
		PayData payData = new PayData();
		payData.setAppId(appid);
		payData.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));
		payData.setPackages("prepay_id=" + prepay_id);
		payData.setSignType("RSA");
		payData.setTimeStamp(new Date().getTime());

		String ascStr = buildSignMessage(payData);
		String paySign = "";
		try {
			paySign = encryptByPrivateKey(ascStr, keyPath);
			payData.setPaySign(paySign);
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("无效的密钥格式");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("当前Java环境不支持RSA", e);
		} catch (InvalidKeySpecException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("无效的密钥");
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("加密数据异常");
		}
		return payData;

	}

	/**
	 * 微信调用支付签名明文
	 * 
	 * @param appid
	 * @param body
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private static String buildSignMessage(PayData payData) {
		try {
			String signStr = payData.getAppId() + "\n" + payData.getTimeStamp() + "\n" + payData.getNonceStr() + "\n"
					+ payData.getPackages() + "\n";
			return signStr;
		} catch (Exception e) {
			// TODO: handle exception
			return "";
		}

	}

	/**
	 * 私钥签名
	 *
	 * @param data
	 *            需要加密的数据
	 * @param privateKey
	 *            私钥
	 * @return 加密后的数据
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 * @throws SignatureException
	 * @throws InvalidKeyException
	 * @throws Exception
	 *             异常信息
	 */
	public static String encryptByPrivateKey(String data, String privateKeyPath) throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
		PrivateKey priKey;
		try {
			priKey = Pem.loadPrivateKey(privateKeyPath);
			java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
			signature.initSign(priKey);
			signature.update(data.getBytes(StandardCharsets.UTF_8));
			byte[] signed = signature.sign();
			String sign = Base64.getEncoder().encodeToString(signed);
			return sign;
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException("找不到文件");
		}

	}

}
